package com.mspbots.cw.amqp.receivers;

import com.fasterxml.jackson.databind.JsonNode;
import com.mspbots.cw.amqp.BaseUserReceiver;
import com.mspbots.cw.amqp.model.ExceptionLog;
import com.mspbots.cw.amqp.model.SheetsUser;
import com.mspbots.cw.amqp.model.UsersMapping;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.r2dbc.query.Criteria;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * com.mspbots.core.amqp.receivers.TSheetsUsersReceiver
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2019/7/30
 */
@Log4j2
@Component
public class SheetsUsersReceiver extends BaseUserReceiver {

    @RabbitListener(queues = "sheet.users")
    public void processMessage(SheetsUser content) {
        try{
            Optional<Long> id = this.databaseClient.execute("SELECT id FROM tsheet_users " +
                    "WHERE  sheet_user_id=:sheetUserId AND tenant_id =:tenantId")
                    .as(Long.class)
                    .bind("tenantId", content.getTenantId())
                    .bind("sheetUserId", content.getSheetUserId())
                    .fetch().one().blockOptional(Duration.ofSeconds(10));
            if (id.isPresent() && id.get() > 0) {
                content.setId(id.get());
                content.setSyncTime(LocalDateTime.now());
                this.databaseClient.update().table(SheetsUser.class)
                        .using(content).fetch().rowsUpdated().block();
            } else {
                this.databaseClient.insert().into(SheetsUser.class).using(content).fetch().rowsUpdated().block();
            }

            this.databaseClient.select().from(SheetsUser.class)
                    .matching(Criteria.where("tenant_id").is(content.getTenantId())
                            .and("sheet_user_id").is(content.getSheetUserId())).fetch().one()
                    .subscribe(result -> {
                        this.autoMapping(result);
                        log.debug("TSheet user save success.{}", result);
                    }, err -> log.error("TSheet user is error,msg: {}", err.getMessage()));
        }catch (Exception e){
            this.databaseClient.insert().into(ExceptionLog.class).using(
                    ExceptionLog.builder()
                            .tenantId(content.getTenantId().intValue())
                            .name("sheet.users")
                            .status(500)
                            .type("sheet.users")
                            .msg(e.getMessage()+","+objectMapper.convertValue(content, JsonNode.class).toString() )
                            .build()
            ).fetch().rowsUpdated().block(Duration.ofSeconds(10));
        }

    }

    private void autoMapping(SheetsUser user) {
        this.databaseClient.execute("SELECT d.id AS usersId,b.id AS cwUserId,c.teams_user_id AS teamsUserId,a.id AS tsheetUserId " +
                "FROM tsheet_users  a LEFT JOIN tenant_user b ON a.email ILIKE b.email  AND a.tenant_id=b.tenant_id  " +
                "LEFT JOIN tenant_teams_user c ON a.email ILIKE c.teams_mail  AND a.tenant_id=c.tenant_id " +
                "LEFT JOIN users d ON a.email ILIKE d.email  AND a.tenant_id=d.tenant_id " +
                "WHERE a.tenant_id=:tenantId AND a.id = :id LIMIT 1")
                .bind("id", user.getId())
                .bind("tenantId", user.getTenantId())
                .map((row, rowMetadata) -> {
                    Map<String, Object> resultMap = new HashMap<>(3);
                    resultMap.put("usersId", row.get("usersId"));
                    resultMap.put("cwUserId", row.get("cwUserId"));
                    resultMap.put("teamsUserId", row.get("teamsUserId"));
                    resultMap.put("tsheetUserId", row.get("tsheetUserId"));
                    return resultMap;
                }).all().flatMap(map -> {
            StringBuilder stringBuffer = new StringBuilder("where tenant_id = " + user.getTenantId());
            UsersMapping usersMapping = UsersMapping.builder().tenantId(user.getTenantId())
                    .sheetUserId(user.getId()).build();

            stringBuffer.append(" and ( sheet_user_id =").append(user.getId());

            if (!ObjectUtils.isEmpty(map.get("cwUserId"))) {
                stringBuffer.append(" or tenant_user_id =").append(map.get("cwUserId"));
                usersMapping.setTenantUserId(Long.valueOf(map.get("cwUserId").toString()));
            }
            if (!ObjectUtils.isEmpty(map.get("teamsUserId"))) {
                stringBuffer.append(" or teams_user_id = '").append(map.get("teamsUserId")).append("' ");
                usersMapping.setTeamsUserId(map.get("teamsUserId").toString());
            }
            if (!ObjectUtils.isEmpty(map.get("usersId"))) {
                stringBuffer.append(" or sheet_user_id = ").append(map.get("usersId"));
                usersMapping.setUserId(Integer.valueOf(map.get("usersId").toString()));
            }
            stringBuffer.append(")");

            return this.databaseClient.execute("SELECT * FROM tenant_user_mapping " + stringBuffer.toString())
                    .as(UsersMapping.class)
                    .fetch().all()
                    .flatMap(mapping -> {
                        if (ObjectUtils.isEmpty(mapping.getSheetUserId())) {
                            mapping.setSheetUserId(user.getId());
                        }
                        return this.databaseClient.update().table(UsersMapping.class)
                                .using(mapping).fetch().rowsUpdated();
                    }).switchIfEmpty(this.databaseClient.insert().into(UsersMapping.class).using(usersMapping)
                            .fetch().rowsUpdated());
        }).subscribe(result -> log.debug("Sheets user mapping success.{}", result),
                err -> log.error("TSheet user mapping error,msg: {}", err.getMessage()));

    }
}
